package main

import (
	"context"
	"crypto/tls"
	"sync"
	"time"

	"gitee.com/doraemon1996/bunny/log"
	"gitee.com/doraemon1996/bunny/network"
	"gitee.com/doraemon1996/bunny/structure"
	"gitee.com/doraemon1996/bunny/timer"
)

type Server struct {
	structure.Control
	Encoder
	MemInfo

	ln      network.Listener        // 监听器
	locker  sync.RWMutex            // 连接管理器读写锁
	connMng map[uint64]network.Conn // 连接管理器
}

// 启动监听端口，等待连接到来
func (s *Server) Start() {
	s.Control.Start(func() {
		s.ln.Start(s.OnConnect)
	})
}

// 清理连接，关闭监听
func (s *Server) Stop() {
	s.Control.Stop(func() {
		s.ln.Stop()
		s.locker.Lock()
		mng := s.connMng
		s.connMng = make(map[uint64]network.Conn)
		s.locker.Unlock()

		for _, conn := range mng {
			conn.Stop(nil)
		}
	})
}

// 新连接事件处理
func (s *Server) OnConnect(conn network.Conn) {
	log.Infof("OnConnect %v", conn)
	conn.Init(s.Context())

	if s.Closed() {
		log.Warnf("OnConnect from %v, but Server closed. Will discard it", conn)
		conn.Stop(nil)
		return
	}

	s.locker.Lock()
	defer s.locker.Unlock()

	if _, exist := s.connMng[conn.ID()]; exist {
		log.Errorf("OnConnect %v exist, will discard the new one", conn)
		conn.Stop(nil)
		return
	}

	s.connMng[conn.ID()] = conn

	conn.Start(s)
}

// 消息事件处理
func (s *Server) OnMessage(conn network.Conn, msg any) {
	length := 0
	switch val := msg.(type) {
	case string:
		length = len(val)
	case *network.UDPPacket:
		length = len(val.Message.(string))
	case *network.WSPacket:
		length = len(val.Message.(string))
	}
	log.Debugf("OnMessage from %v. Length: %v", conn, length)

	conn.Send(msg)
}

// 定时任务事件处理
func (s *Server) OnCrontab(conn network.Conn) {
	log.Debugf("OnCrontab %v", conn)
}

// 错误事件处理
func (s *Server) OnError(conn network.Conn, err network.Error) bool {
	log.Errorf("OnError %v", err)

	return err.ErrorType() == network.ErrorType_Underlying
}

// 连接断开处理
func (s *Server) OnDisconnect(conn network.Conn, err network.Error) {
	if err != nil {
		log.Warnf("OnDisconnect abnormal disconnect %v with err: %s", conn, err)
	} else {
		log.Infof("OnDisconnect normal disconnect %v", conn)
	}

	s.locker.Lock()
	delete(s.connMng, conn.ID())
	s.locker.Unlock()

	switch conn.Type() {
	case network.Connection_WS, network.Connection_WSS:
		wsConn := conn.(*network.WSConn)
		wsConn.WriteClose(1000, "Bye! Bye!")
	}
}

func NewServer(networkStr, addressStr, wsPath, wssCertPath, wssKeyPath string) *Server {
	s := new(Server)
	s.Init(context.Background(), func() {
		s.connMng = make(map[uint64]network.Conn)
	})

	optionList := make([]network.Option, 0)
	optionList = append(optionList, network.WithNetwork(networkStr))
	optionList = append(optionList, network.WithAddress(addressStr))
	optionList = append(optionList, network.WithWSPath(wsPath))
	if networkStr == "wss" {
		cert, err := tls.LoadX509KeyPair(wssCertPath, wssKeyPath)
		if err != nil {
			log.Fatalf("LoadX509KeyPair with %s %s failed. err: %v", wssCertPath, wssKeyPath, err)
		}
		tlsCfg := &tls.Config{
			Certificates:       []tls.Certificate{cert},
			InsecureSkipVerify: true,
		}
		optionList = append(optionList, network.WithWSTlsConfig(tlsCfg))
	}

	ln, err := network.Listen(s.Context(), optionList...)
	if err != nil {
		log.Fatalf("listen %s %s failed. err: %v", networkStr, addressStr, err)
	}

	s.ln = ln
	return s
}

func RunServer(networkStr, addressStr, wsPath, wssCertPath, wssKeyPath string) {
	s := NewServer(networkStr, addressStr, wsPath, wssCertPath, wssKeyPath)
	s.Start()

	var taskID uint64
	taskID = timer.Interval(time.Second, func() {
		if s.Opening() {
			s.PrintMemInfo()
		} else {
			timer.Remove(taskID)
		}
	})

	log.ExitHandlerInsert(func() { s.Stop() })
}
